package org.cary.lucene.search;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.IntPoint;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.BooleanQuery.Builder;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.cary.common.Page;
import org.cary.lucene.SearchBean;
import org.cary.lucene.StoreBean;
import org.cary.lucene.StoreQueueBean;
import org.cary.lucene.query.QueryFactory;
import org.cary.utils.StringUtils;

public class LuceneSearchEngine extends AbstractSearchEngine {

	// 索引分类
	public static final String TYPE_MESSAGE = "1";// 消息
	public static final String TYPE_INFOMATION = "2";// 资讯
	public static final String TYPE_SHARING = "3";// 文章

	// 索引操作类型
	public static final int INDEX_ADD = 1;
	public static final int INDEX_UPDATE = 2;
	public static final int INDEX_DELETE = 3;
	public static final int INDEX_DELETE_ALL_BY_SWITCHBOARD = 4;
	public static final int INDEX_DELETE_QUERY = 5;

	@Override
	public void addIndex(StoreBean bean) throws Exception {
		if (bean == null) return;
		addIndex(Collections.singletonList(bean));
	}

	/**
	 * 增加修改索引时，会引起索引锁定，可能要少调用本方法
	 */
	@Override
	public void addIndex(List<StoreBean> beans) throws Exception {
		executeChange(beans, INDEX_ADD);
	}

	@Override
	public void updateIndex(StoreBean bean) throws Exception {
		if (bean == null) return;
		updateIndexs(Collections.singletonList(bean));

	}

	@Override
	public void updateIndexs(List<StoreBean> beans) throws Exception {
		executeChange(beans, INDEX_UPDATE);
	}

	@Override
	public void deleteIndex(StoreBean bean) throws Exception {
		if (bean == null) return;
		deleteIndexs(Collections.singletonList(bean));
	}

	@Override
	public void deleteIndexs(List<StoreBean> beans) throws Exception {
		executeChange(beans, INDEX_DELETE);
	}

	@Override
	public Page<StoreBean> search(Query query, boolean isHighlighter, Page<StoreBean> page) throws Exception {
		List<StoreBean> resultList = new ArrayList<StoreBean>();
		IndexSearcher searcher = getSearcher();
		if (searcher == null) {
			page.setTotalRecords(0);
			page.setResults(resultList);
			return page;
		}
		TopDocs topDocs = searcher.search(query, 300);
		int totalHits = topDocs.totalHits;
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;

		// 查询起始记录位置
		int begin = (page.getPage() - 1) * page.getPer();
		// 查询终止记录位置
		int end = Math.min(begin + page.getPer(), scoreDocs.length);
		int docID;
		Document hitDoc = null;
		StoreBean storeBean = null;
		String scope = null;
		for (int i = begin; i < end; i++) {
			storeBean = new StoreBean();
			docID = scoreDocs[i].doc;
			hitDoc = searcher.doc(docID);

			storeBean.setId(hitDoc.get("id"));
			String _createDate = hitDoc.get("createDate");
			if (StringUtils.isNotBlank(_createDate)) {
				try {
					storeBean.setCreateDate(Long.parseLong(_createDate));
				} catch (Exception e) {
				}
			}

			storeBean.setIndexType(hitDoc.get("indexType"));
			storeBean.setTitle(hitDoc.get("title"));
			scope = hitDoc.get("scope");
			if (StringUtils.isNotBlank(scope)) {
				storeBean.setScope(Integer.valueOf(scope));
			}
			storeBean.setSource_type(hitDoc.get("source_type"));
			storeBean.setDescrip(hitDoc.get("descrip"));
			storeBean.setContent(hitDoc.get("content"));
			storeBean.setSource_id(hitDoc.get("source_id"));
			storeBean.setUser_id(hitDoc.get("user_id"));
			storeBean.setDb(hitDoc.get("db"));
			resultList.add(storeBean);
		}
		page.setTotalRecords(totalHits);
		page.setResults(resultList);
		return page;
	}

	public List<StoreBean> search(Query query, boolean isHighlighter) throws Exception {
		List<StoreBean> resultList = new ArrayList<StoreBean>();
		IndexSearcher searcher = getSearcher();
		TopDocs topDocs = searcher.search(query, 300);
		int totalHits = topDocs.totalHits;
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;

		int docID;
		Document hitDoc = null;
		StoreBean storeBean = null;
		String scope = null;
		for (int i = 0; i < totalHits; i++) {
			// 后台报错 java.lang.ArrayIndexOutOfBoundsException: 300
			if (i >= scoreDocs.length) {
				break;
			}
			storeBean = new StoreBean();
			docID = scoreDocs[i].doc;
			hitDoc = searcher.doc(docID);

			storeBean.setId(hitDoc.get("id"));
			String _createDate = hitDoc.get("createDate");
			if (StringUtils.isNotBlank(_createDate)) {
				try {
					storeBean.setCreateDate(Long.parseLong(_createDate));
				} catch (Exception e) {
				}
			}

			storeBean.setIndexType(hitDoc.get("indexType"));
			storeBean.setTitle(hitDoc.get("title"));
			scope = hitDoc.get("scope");
			if (StringUtils.isNotBlank(scope)) {
				storeBean.setScope(Integer.valueOf(scope));
			}
			storeBean.setSource_type(hitDoc.get("source_type"));
			storeBean.setDescrip(hitDoc.get("descrip"));
			storeBean.setContent(hitDoc.get("content"));
			storeBean.setSource_id(hitDoc.get("source_id"));
			storeBean.setUser_id(hitDoc.get("user_id"));
			storeBean.setDb(hitDoc.get("db"));
			resultList.add(storeBean);
		}
		return resultList;
	}

	// 应用根据id来查询
	public StoreBean searchById(SearchBean bean, boolean isHighlighter) throws Exception {
		StoreBean storeBean = null;
		// 这里需要交集
		Builder builder = new Builder();
		Query idQuery = new TermQuery(new Term("id", bean.getKeyword()));
		builder.add(idQuery, BooleanClause.Occur.MUST);
		if (!StringUtils.isBlank(bean.getIndexType())) {
			Query indexTypeQuery = new TermQuery(new Term("indexType", bean.getIndexType()));
			builder.add(indexTypeQuery, BooleanClause.Occur.MUST);
		}

		BooleanQuery query = builder.build();
		IndexSearcher searcher = getSearcher();
		TopDocs topDocs = searcher.search(query, 10);
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		if (topDocs.totalHits > 0) {
			storeBean = new StoreBean();
			int docID = scoreDocs[0].doc;
			Document hitDoc = searcher.doc(docID);
			storeBean.setId(hitDoc.get("id"));
			String _createDate = hitDoc.get("createDate");
			if (StringUtils.isNotBlank(_createDate)) {
				try {
					storeBean.setCreateDate(Long.parseLong(_createDate));
				} catch (Exception e) {
				}
			}
			storeBean.setIndexType(hitDoc.get("indexType"));
			storeBean.setTitle(hitDoc.get("title"));
			storeBean.setSource_type(hitDoc.get("source_type"));
			storeBean.setContent(hitDoc.get("content"));
			storeBean.setSource_id(hitDoc.get("source_id"));
			storeBean.setUser_id(hitDoc.get("user_id"));
			storeBean.setDb(hitDoc.get("db"));
		}
		return storeBean;
	}

	/**
	 * 对消费都进行消费
	 * @param beans
	 * @throws Exception
	 */
	public void changeIndexs(List<StoreQueueBean> beans, int lucencConsumerNum) throws Exception {
		if (beans == null || beans.size() < 1) return;
		System.err.println("lucene初始化索引集合：" + beans.size());
		StoreBean tmpBean = beans.get(0);
		// 取出第一个，分析他所需要存储的字段
		IndexWriter writer = null;
		try {
			Directory storeDir = getStoreDir();
			Analyzer analyzer = getAnalyzer();
			IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
			iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
			writer = new IndexWriter(storeDir, iwc);
			Document doc = null;
			int i = 0;
			int exeType;
			for (StoreQueueBean queueBean : beans) {
				i++;
				// 仅添加与更新时，会操作doc
				exeType = queueBean.getExeType();
				doc = new Document();
				changeIndex(exeType, queueBean, doc, writer);

				// 如果内容超过lucencConsumerNum条时，先提交
				// 如果不够lucencConsumerNum条，由write.close();提交
				if (i == lucencConsumerNum) {
					i = 0;
					writer.commit();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			destryIndexWriter(writer);
		}
	}

	// 内容索引更新，
	private void executeChange(List<StoreBean> beans, int exeType) throws Exception {
		if (beans == null || beans.size() < 1) return;

		StoreBean tmpBean = beans.get(0);

		// 取出第一个，分析他所需要存储的字段
		IndexWriter writer = null;
		try {
			Directory storeDir = getStoreDir();
			Analyzer analyzer = getAnalyzer();
			IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
			iwc.setOpenMode(OpenMode.CREATE_OR_APPEND);
			writer = new IndexWriter(storeDir, iwc);
			Document doc = null;
			int i = 0;
			for (StoreBean storeBean : beans) {
				i++;
				doc = new Document();
				changeIndex(exeType, storeBean, doc, writer);

				// 如果内容超过100条时，先提交
				// 如果不够100条，由write.close();提交
				if (i == 100) {
					i = 0;
					writer.commit();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			destryIndexWriter(writer);
		}
	}

	private void changeIndex(int exeType, StoreBean queueBean, Document doc, IndexWriter writer) throws CorruptIndexException, IOException {
		if (exeType < INDEX_DELETE) {
			doc.add(new TextField("id", queueBean.getId(), Store.YES));
			if (queueBean.getCreateDate() != null) {
				LongPoint createDate = new LongPoint("createDate", queueBean.getCreateDate());
				doc.add(createDate);
			}
			doc.add(new StringField("indexType", queueBean.getIndexType(), Store.YES));

			String sourceType = queueBean.getSource_type();
			if (StringUtils.isNotBlank(sourceType)) {
				doc.add(new StringField("source_type", sourceType, Store.YES));
			}
			String persons = queueBean.getPersons();
			if (StringUtils.isNotBlank(persons)) {
				doc.add(new StringField("persons", persons, Store.YES));
			}
			Integer scope = queueBean.getScope();
			if (scope != null) {
				doc.add(new IntPoint("scope", queueBean.getScope()));
			}

			String source_id = queueBean.getSource_id();
			if (source_id != null) {
				doc.add(new StringField("source_id", source_id, Store.YES));
			}

			String user_id = queueBean.getUser_id();
			if (user_id != null) {
				doc.add(new StringField("user_id", user_id, Store.YES));
			}

			String db = queueBean.getDb();
			if (db == null) {
				doc.add(new StringField("db", "1", Store.YES));
			} else {
				doc.add(new StringField("db", db, Store.YES));
			}

			doc.add(new StringField("title", queueBean.getTitle(), Store.YES));
			doc.add(new StringField("descrip", queueBean.getDescrip(), Store.YES));
			doc.add(new StringField("content", queueBean.getContent(), Store.YES));
		}
		switch (exeType) {
		case INDEX_ADD:
			writer.addDocument(doc);
			break;
		case INDEX_UPDATE:
			writer.updateDocument(new Term("id", queueBean.getId()), doc);
			break;
		case INDEX_DELETE:
			writer.deleteDocuments(new Term("id", queueBean.getId()));
			break;
		case INDEX_DELETE_QUERY:
			if (queueBean.getQuery() != null) {
				writer.deleteDocuments(queueBean.getQuery());
			}
			break;
		case INDEX_DELETE_ALL_BY_SWITCHBOARD:
			// 这种方法删除一次，提交一次
			writer.deleteDocuments(new Term("user_id", queueBean.getUser_id()));
			writer.commit();
			break;
		}
		System.out.println("索引信息--->exeType:" + exeType + ",  queueBean" + queueBean);

	}

	private void destryIndexWriter(IndexWriter write) {
		if (write != null) {
			try {
				// 强制优化，del文件就没了，回收站清空
				write.forceMergeDeletes();
				write.close();
				super.reSetSearcher();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public static void main(String[] args) throws Exception {
		// 写入索引
		/*IndexQueueManager indexManager = IndexQueueManager.getInstance();
		indexManager.start();
		StoreQueueBean queueBean = new StoreQueueBean(INDEX_ADD);
		queueBean.setId("aaa");
		queueBean.setTitle("我是一个兵");
		queueBean.setDescrip(" 兵 对方"); //需要检索的东西  
		queueBean.setSource_type("1");//类型  
		queueBean.setSource_id("source_id");
		queueBean.setScope(1);//范围 组内、组外
		queueBean.setPersons("15332113553");//共享给谁，也就是这个信息谁能看到
		queueBean.setContent("我是一个兵 撒旦发射点发的法国是大法官风格使得法国使得法国啥都干祷告");//实际内容
		queueBean.setIndexType(LuceneSearchEngine.TYPE_MESSAGE);
		queueBean.setCreateDate(System.currentTimeMillis());
		indexManager.indexChanged(queueBean, INDEX_ADD);
		Thread.sleep(6000);*/
		// 使用搜索索引
		LuceneSearchEngine engine = new LuceneSearchEngine();
		Page<StoreBean> page = new Page<StoreBean>(1, 5);
		SearchBean searchBean = new SearchBean("一", null);
		searchBean.setIndexType(LuceneSearchEngine.TYPE_MESSAGE);
		long start = System.currentTimeMillis();
		Query query = QueryFactory.buildShareQuery(searchBean);
		System.out.println(query);
		page = engine.search(query, false, page);
		System.out.println("总共：" + page.getTotalRecords() + " ;第一次用时：" + (System.currentTimeMillis() - start));
		List<StoreBean> r = (List<StoreBean>) page.getResults();

		for (StoreBean b : r) {
			System.out.println(b);
		}
		//indexManager.stop();

	}
}
